package com.rc.saas.mini.config;

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;

/**
 * Created by Sven on 2021/11/10
 */
@Configuration
public class Redis2Config {
    @Value("${spring.redis2.host}")
    private String host;

    @Value("${spring.redis2.port}")
    private Integer port;

    @Value("${spring.redis2.password}")
    private String password;

    @Value("${spring.redis2.database}")
    private int database;

    //连接超时时间（单位为毫秒）
    @Value("${spring.redis2.timeout}")
    private int timeOut;

    //使用lettuce连接池配置
    //连接池中的最大空闲连接
    @Value("${spring.redis2.lettuce.pool.max-active}")
    private int maxActive;

    //连接池最大阻塞等待时间
    @Value("${spring.redis2.lettuce.pool.max-wait}")
    private int maxWait;

    //最大核心线程数
    @Value("${spring.redis2.lettuce.pool.max-idle}")
    private int maxIdle;

    //最小核心线程数
    @Value("${spring.redis2.lettuce.pool.min-idle}")
    private int minIdle;

    /**
     * redisTemplate2
     * RedisTemplate使用LettuceConnectionFactory连接工厂
     */
    @Bean("redisTemplate2")
    public RedisTemplate redisTemplate2(@Qualifier("jedisConnectionFactory2") JedisConnectionFactory redisConnectionFactory2) {
        //在SpringBoot2.0之后，spring容器是自动的生成了StringRedisTemplate和RedisTemplate<Object,Object>，
        //可以直接注入,但是在实际使用中，我们大多不会直接使用RedisTemplate<Object,Object>，
        // 而是会对key,value进行序列化

        //在自定义序列化过程，GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializer大部分时候表现没有区别，实际上如果对象中有LinkedHashMap时候，后者会出错，这个以前坑了我很久，自我怀疑了很久。
        //建议使用GenericJackson2JsonRedisSerializer来序列化。
        //GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializer都有一个问题，无法反序列化接口的动态代理类，原因应该是动态代理类没有缺省构造函数，对JPA的自定义结果集支持不好，对Page分页支持不好。

        RedisTemplate redisTemplate2 = new RedisTemplate();
        redisTemplate2.setConnectionFactory(redisConnectionFactory2);
        //配置序列化方式
        redisTemplate2.setKeySerializer(new StringRedisSerializer());
        //使用fastjson 代替 GenericJackson2JsonRedisSerializer
        redisTemplate2.setValueSerializer(new GenericFastJsonRedisSerializer());
        redisTemplate2.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate2.setHashValueSerializer(new GenericFastJsonRedisSerializer());

        return redisTemplate2;
    }

    /**
     * jedisPoolConfig2
     * 配置redis连接池的配置 JedisPoolConfig
     */
    @Bean("jedisPoolConfig2")
    public JedisPoolConfig jedisPoolConfig2() {
        //链接池test参数【BaseObjectPoolConfig】：
        //1. 默认是false，创建一个链接时检测是否链接有效，无效则剔除，并尝试继续获取新链接。
        //private boolean testOnCreate = DEFAULT_TEST_ON_CREATE;
        //2. 默认是false，借取一个链接时检测是否有效，无效则剔除，并尝试继续获取新链接。
        //private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW;
        //3.默认为false，归还一个对象时检测是否有效，无效则不放入链接池内。
        //private boolean testOnReturn = DEFAULT_TEST_ON_RETURN;
        //4.默认为false，指明空闲链接是否需要被【空闲链接回收器】【evict方法】检测，检测出链接无效则被移除。
        //private boolean testWhileIdle = DEFAULT_TEST_WHILE_IDLE;

        //<!-- redis连接池的配置 -->
        //<bean id="jedisPoolConfig2" class="redis.clients.jedis.JedisPoolConfig">
        //    <property name="maxTotal" value="${redis.session.pool.maxTotal}"/>
        //    <property name="maxIdle" value="${redis.session.pool.maxIdle}"/>
        //    <property name="minIdle" value="${redis.session.pool.minIdle}"/>
        //    <property name="testOnBorrow" value="${redis.session.pool.testOnBorrow}"/>
        //    <property name="testOnReturn" value="${redis.session.pool.testOnReturn}"/>
        //</bean>

        JedisPoolConfig jedisPoolConfig2 = new JedisPoolConfig();
        //最大连接数
        jedisPoolConfig2.setMaxTotal(maxActive);
        //当池内没有可用连接时，最大等待时间
        jedisPoolConfig2.setMaxWaitMillis(maxWait);

        //最小空闲连接数
        jedisPoolConfig2.setMaxIdle(maxIdle);
        jedisPoolConfig2.setMinIdle(minIdle);

        jedisPoolConfig2.setTestOnBorrow(true);
        jedisPoolConfig2.setTestOnReturn(false);
        jedisPoolConfig2.setTestWhileIdle(true);
        jedisPoolConfig2.setTestOnCreate(false);

        return jedisPoolConfig2;
    }

    /**
     * jedisConnectionFactory2
     */
    @Bean("jedisConnectionFactory2")
    public JedisConnectionFactory jedisConnectionFactory2() {
        //单机模式
        RedisStandaloneConfiguration redisStandaloneConfiguration2 = new RedisStandaloneConfiguration();
        //IP
        redisStandaloneConfiguration2.setHostName(host);
        //端口
        redisStandaloneConfiguration2.setPort(port);
        //密码
        if (StringUtils.isNotEmpty(password)) {
            redisStandaloneConfiguration2.setPassword(password);
        }

        JedisClientConfiguration.DefaultJedisClientConfigurationBuilder jpcb2 =
                (JedisClientConfiguration.DefaultJedisClientConfigurationBuilder) JedisClientConfiguration.builder();
        jpcb2.poolConfig(jedisPoolConfig2());

        //读取超时时间
        jpcb2.readTimeout(Duration.ofMillis(timeOut));

        //设置连接超时时间
        jpcb2.connectTimeout(Duration.ofMillis(timeOut));


        //设置是否使用池不需要传参数了，这个方法会将这个属性置为true。
        jpcb2.usePooling();
        JedisClientConfiguration jedisClientConfiguration2 = jpcb2.build();
        JedisConnectionFactory factory2 = new JedisConnectionFactory(redisStandaloneConfiguration2, jedisClientConfiguration2);
        return factory2;
    }

}
